
#ifndef _DLL_H
#define _DLL_H

// todo: API should work in UNICODE

#include <vcl.h>


namespace Dll
{

__declspec(selectany) UnicodeString ErrDllMsg = L"Error loading library: %s";
__declspec(selectany) UnicodeString ErrProcMsg = L"Error found address procedure %s in library %s";


class EDllError: public Exception
{
private:
    UnicodeString m_Name;
    UnicodeString m_NameProc;

public:
    __fastcall EDllError(const UnicodeString u_Fmt ,const UnicodeString u_Name) :
                   Exception(Format(u_Fmt,ARRAYOFCONST((u_Name)))), m_Name(u_Name){};
    __fastcall EDllError(const UnicodeString u_Fmt ,const UnicodeString u_Name, const UnicodeString u_NameProc) :
                   Exception(Format(u_Fmt,ARRAYOFCONST((u_NameProc, u_Name)))), m_Name(u_Name), m_NameProc(u_NameProc){};
    __property UnicodeString Name = { read=m_Name, write=m_Name };
    __property UnicodeString NameProc = { read=m_NameProc, write=m_NameProc };
};





class EDllProcError: public EDllError
{
public:
    __fastcall EDllProcError(const UnicodeString u_Fmt ,const UnicodeString u_Name, const UnicodeString u_NameProc) :
                   EDllError(u_Fmt ,u_Name, u_NameProc){};
};


class TDll
{
   HINSTANCE m_DLLInstance;
public:
   TDll(){};
   TDll(const UnicodeString u_Name)
   {
     TDllLoad(u_Name);
   }

   TDllLoad(const UnicodeString u_Name)
   {
#pragma option push -w-pia
     AnsiString a_NameTmp(u_Name);
     if (!(m_DLLInstance = LoadLibrary(a_NameTmp.c_str())))
#pragma option pop
        throw(EDllError(ErrDllMsg, u_Name));
     mu_Name = u_Name;
   };
   ~TDll()
   {
     if (m_DLLInstance) FreeLibrary(m_DLLInstance);
   }

   operator HINSTANCE() const { return m_DLLInstance; }
   UnicodeString mu_Name;
};



class TDllProc
{
public:
   TDllProc(const TDll& a_Dll, const UnicodeString u_NameProc)
   {
#pragma option push -w-pia
      AnsiString a_NameProcTmp(u_NameProc);
      if(!(m_Proc = GetProcAddress(HINSTANCE(a_Dll),a_NameProcTmp.c_str())))
#pragma option pop
      throw(EDllProcError(ErrProcMsg, a_Dll.mu_Name, u_NameProc));
   }

public:
   FARPROC m_Proc;
};


//-----------------------------------------------------------------------------------
// Template functions:
//
// void DllFunction();

class TDllProcV0 : public TDllProc
{
public:
   TDllProcV0(const TDll& a_Dll, const char* a_Name) :TDllProc(a_Dll,a_Name){}
   void operator() ()
   {
      typedef void (* TProc)();
      ((TProc)m_Proc)();
   }
};
// ----------------------------------------------------------------------------------

// <r_param> DllFunction();
//
template <class R>
class TDllProc0 : public TDllProc
{
public:
   TDllProc0(const TDll& a_Dll, const char* a_Name) : TDllProc(a_Dll,a_Name) {}
   R operator ()()
   {
      typedef R ( * TProc)();
      return ((TProc)m_Proc)();
   }
};
// ----------------------------------------------------------------------------------

//  void DllFunction(param1, param2);
//
template <class P1, class P2>
class TDllProcV2 : public TDllProc
{
public:
   TDllProcV2(const TDll& a_Dll, const char* a_Name) : TDllProc(a_Dll,a_Name) {}
   void operator ()(P1 p1, P2 p2)
   {
      typedef void (__cdecl* TProc)(P1, P2 );
      ((TProc)m_Proc)(p1, p2);
   }
};
// ----------------------------------------------------------------------------------


// <r_param> DllFunction(param1, param2);
//
template <class R, class P1, class P2>
class TDllProc2 : public TDllProc
{
public:

   TDllProc2(const TDll& a_Dll, const char* a_Name) : TDllProc(a_Dll,a_Name) {}
   R operator ()(P1 p1, P2 p2)
   {
      typedef R ( __cdecl *TProc)(P1, P2);
      return ((TProc)m_Proc)(p1, p2);
   }
};



} // namespace Dll


#endif //_DLL_H

